import { _decorator, Component, Constructor, Node, NodeEventType, Tween, UITransform } from "cc";
import { ScrollList } from "./ScrollList";
const { ccclass } = _decorator;

/**
 * 滚动列表单元格
 */
@ccclass("ScrollListCell")
export class ScrollListCell<T = any> extends Component {

    /** 所在列表 */
    list: ScrollList<T>
    /** 在列表数据的index */
    idx: number
    /** 在列表显示的index */
    showIdx: number
    /** 位置节点 在列表的位置由此节点决定的 */
    posNode: Node
    /** 动画节点 在位置节点下 实际内容节点在此节点下 */
    animNode: Node;
    /** 动画节点的变换组件 */
    animTrans: UITransform;
    /** 组件缓存 */
    compCache: { [comp: string]: Component } = {};
    /** 子节点/组件缓存 */
    childCache: { [childName: string]: { node: Node, comp: { [compName: string]: Component } } } = {};

    /** 单元格数据 */
    get data() { return this.list.list[this.idx]; }
    set data(v: T) { this.list.list[this.idx] = v; }

    /** 是否选中 */
    get selected() { return this.idx == this.list.selectedIdx; }

    /**
     * 创建出来时的初始化,由ScrollList调用
     */
    onCreate(list: ScrollList) {
        this.list = list;
        this.posNode = new Node("CellPos");
        this.posNode.layer = list.node.layer;
        this.posNode.addChild(this.animNode = new Node("CellAnim"));
        this.animTrans = this.animNode.addComponent(UITransform);
        this.animTrans.setContentSize(this.getComp(UITransform).contentSize);
        this.animNode.layer = list.node.layer;
        this.animNode.setPosition(this.animTrans.width * 0.5, -this.animTrans.height * 0.5);
        this.animNode.addChild(this.node);
        this.node.on(NodeEventType.TOUCH_END, this.onTouchEnd, this);
        return this;
    }

    /**
     * 被回收时由ScrollList调用
     */
    onRecy() {
        this.node.off(NodeEventType.TOUCH_END, this.onTouchEnd);
        Tween.stopAllByTarget(this.animNode);
        this.posNode.removeFromParent();
        return this;
    }

    private onTouchEnd() {
        if (this.list.isDraging) return;
        this.list.selectedIdx = this.idx;
    }

    /**
     * 选中当前单元格
     * @param doCallback 必定触发回调(false的情况下如果之前选中的就是当前单元格就不会触发选中和渲染的回调)
     */
    select(doCallback = false) {
        if (doCallback) this.list.setSelectedIdx(this.idx);
        else this.list.selectedIdx = this.idx;
    }

    /**
     * 获取节点组件,获取一次后会缓存起来
     * @param comp 组件构造器
     * @returns 
     */
    getComp<T extends Component>(comp: Constructor<T>): T {
        let cache = this.compCache[comp.name];
        if (cache) return cache as T;
        cache = this.getComponent(comp);
        return cache && (this.compCache[comp.name] = cache) as T;
    }

    /**
     * 获取子节点,获取一次后会缓存起来
     * @param name 子节点名 按以下规则可取子节点树下的节点"子节点.二级子节点.三级子节点" 
     * @returns 
     */
    getChild(name: string) {
        let cache = this.childCache[name];
        if (cache) return cache.node;
        let isTree = name.includes(".");
        if (isTree) {
            let names = name.split(".");
            let treeName = "";
            let node = this.node;
            for (let i = 0; i < names.length; ++i) {
                cache = this.childCache[name];
                if (cache) {
                    node = cache.node;
                    continue;
                }
                node = node.getChildByName(names[i]);
                if (!node) return null;
                this.childCache[treeName += names[i]] = { node, comp: {} };
                treeName += ".";
            }
            return node;
        } else {
            let node = this.node.getChildByName(name);
            return node && (this.childCache[name] = { node, comp: {} }).node;
        }
    }

    /**
     * 获取子节点,获取一次后会缓存起来
     * @param name 子节点名 按以下规则可取子节点树下的节点"子节点.二级子节点.三级子节点" 
     * @param comp 组件构造器
     * @returns 
     */
    getChildComp<T extends Component>(name: string, comp: Constructor<T>): T {
        let child = this.getChild(name);
        if (!child) return null;
        let cache = this.childCache[name].comp[comp.name];
        if (cache) return cache as T;
        cache = child.getComponent(comp);
        return cache && (this.childCache[name].comp[comp.name] = cache) as T;
    }

    /**
     * 触发渲染时执行
     * 虚方法，子类复写时不需要调用super
     */
    onRender() { }

    /**
     * 触发选中时执行
     * 虚方法，子类复写时不需要调用super
     */
    onSelect() { }

}